﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PubSubLite.Configuration;
using System.Threading;
using PubSubLite.Utils;

namespace PubSubLite.Messaging.Threading
{
    internal class Invoker
    {

        public Invoker(IBusConfiguration configuration)
        {
            Require.Argument.IsNotNull(configuration, "configuration");
            _configuration = configuration;
        }

        private IBusConfiguration _configuration;

        public void Invoke(InvokeArgs args)
        {
            _configuration.Debug("Invoke, ThreadSetting=" + args.InvokeType.ToString());
            if (args.InvokeType == InvokeArgs.InvokeTypes.MethodInfo)
            {
                if (_configuration.ThreadSetting == ThreadSettings.SingleThread)
                {
                    InvokeMethodInfoOnCurrentThread(args);
                }
                else
                {
                    ThreadPool.QueueUserWorkItem(new WaitCallback(InvokeMethodInfoOnSeparateThread), args);
                }
            }
            else if (args.InvokeType == InvokeArgs.InvokeTypes.Delegate)
            {
                if (_configuration.ThreadSetting == ThreadSettings.SingleThread)
                {
                    InvokeDelegateOnCurrentThread(args.Handler, args.Arg);
                }
                else
                {
                    ThreadPool.QueueUserWorkItem(new WaitCallback(InvokeDelegateOnSeparateThread), args);
                }
            }
        }

        private void InvokeMethodInfoOnCurrentThread(InvokeArgs notification)
        {
            _configuration.Debug("invoking methodinfo current thread: " + notification.Args[0].GetType().Name);
            notification.MethodInfo.Invoke(notification.Object, notification.Args);
        }

        private void InvokeMethodInfoOnSeparateThread(object args)
        {
            var a = (InvokeArgs)args;
            _configuration.Debug("invoking methodinfo separate thread: " + ((object[])a.Args)[0].GetType().Name);
            a.MethodInfo.Invoke(a.Object, a.Args);
        }

        private void InvokeDelegateOnCurrentThread(Delegate handler, object args)
        {
            _configuration.Debug("invoking delegate current thread: " + args.GetType().Name);
            handler.DynamicInvoke(args);
        }

        private void InvokeDelegateOnSeparateThread(object arg)
        {
            var a = (InvokeArgs)arg;
            _configuration.Debug("invoking delegate separate thread: " + arg.GetType().Name);
            a.Handler.DynamicInvoke(a.Arg);
        }
    }
}
